/*
The pci.c file implements the low-level PCI bus driver
*/


#include <globals.h>
#include <klib.h>
#include <panic.h>

//Private Includes
#include "include/pci.h"


/* Redefine EXTERN because we'll be defining functions for real here */ 


/* Data structures */

                                /* System Memory Pages */
//dword_t nr_pages;               /* Number of memory pages above the 16 MB mark */


/* Private functions prototypes */
extern int pci_detect(void);
extern int pci_read_config_byte(pci_t *pci,unsigned reg,byte_t *val);
extern int pci_read_config_word(pci_t *pci,unsigned reg,word_t *val);
extern int pci_read_config_dword(pci_t *pci,unsigned reg,dword_t *val);
extern int pci_write_config_byte(pci_t *pci,unsigned reg,byte_t val);
extern int pci_write_config_word(pci_t *pci,unsigned reg,word_t val);
extern int pci_write_config_dword(pci_t *pci,unsigned reg,dword_t val);


/* Functions implementation */

int pci_read_config_byte(pci_t *pci,unsigned reg,byte_t *val)
  {
    outl(PCI_ADDR_REG,
	     0x80000000 | /* "enable configuration space mapping" */
	     ((unsigned long)pci->bus << 16) |	/* b23-b16=bus */
	     ((unsigned)pci->dev << 11) |	/* b15-b11=dev */
	     ((unsigned)pci->fn << 8) |		/* b10-b8 =fn  */
	     (reg & ~3));			/* b7 -b2 =reg */
    *val = inb(PCI_DATA_REG + (reg & 3));   // xxx - is this legit?
    return 0;
  }

int pci_read_config_word(pci_t *pci,unsigned reg,word_t *val)
  {
    outl(PCI_ADDR_REG, 0x80000000 |
	     ((unsigned long)pci->bus << 16) |
	     ((unsigned)pci->dev << 11) |
	     ((unsigned)pci->fn << 8) | (reg & ~3));
    *val = inw(PCI_DATA_REG + (reg & 2));
    return 0;
  }

int pci_read_config_dword(pci_t *pci,unsigned reg,dword_t *val)
  {
    outl(PCI_ADDR_REG, 0x80000000 |
	 ((unsigned long)pci->bus << 16) |
	 ((unsigned)pci->dev << 11) |
	 ((unsigned)pci->fn << 8) | (reg & ~3));
    *val = inl(PCI_DATA_REG + 0);
    return 0;
  }

int pci_write_config_byte(pci_t *pci,unsigned reg,byte_t val)
  {
    outl(PCI_ADDR_REG, 0x80000000 |
	 ((unsigned long)pci->bus << 16) |
	 ((unsigned)pci->dev << 11) |
	 ((unsigned)pci->fn << 8) | (reg & ~3));
    outb(PCI_DATA_REG + (reg & 3), val);
    return 0;
  }

int pci_write_config_word(pci_t *pci,unsigned reg,word_t val)
  {
    outl(PCI_ADDR_REG, 0x80000000 |
	 ((unsigned long)pci->bus << 16) |
	 ((unsigned)pci->dev << 11) |
	 ((unsigned)pci->fn << 8) | (reg & ~3));
    outw(PCI_DATA_REG + (reg & 2), val);
    return 0;
  }

int pci_write_config_dword(pci_t *pci,unsigned reg,dword_t val)
  {
    outl(PCI_ADDR_REG, 0x80000000 |
	 ((unsigned long)pci->bus << 16) |
	 ((unsigned)pci->dev << 11) |
	 ((unsigned)pci->fn << 8) | (reg & ~3));
    outl(PCI_DATA_REG + 0, val);
    return 0;
  }

/* Public Functions implementation */

void pci_initialize(void)
  {
    return;
  }

int pci_detect(void)
  {
    printf("Detecting PCI controller... ");
    /* Use I/O register to see if there's a PCI controller */
    outl(PCI_ADDR_REG, 0x80000000); /* bus 0, dev 0, fn 0, reg 0 */

    if (inl(PCI_ADDR_REG) != 0x80000000)
      {
	printf("not found\n");
	return -1;
      }

    printf("found\n");
    return 0;
  }

static int pci_iterate(pci_t *pci)
  {
    unsigned char hdr_type = 0x80;
    
    /* if first function of this device, check if multi-function device
       (otherwise fn==0 is the _only_ function of this device) */
    if(pci->fn == 0)
      {
	if(pci_read_config_byte(pci, 0x0E, &hdr_type))
	  return -1;	/* error */
      }
    /* increment iterators
       fn (function) is the least significant, bus is the most significant */
    pci->fn++;
    if(pci->fn >= 8 || (hdr_type & 0x80) == 0)
      {
	pci->fn = 0;
	pci->dev++;
	if(pci->dev >= 32)
	  {
	    pci->dev = 0;
	    pci->bus++;
	    //			if(pci->bus > g_last_pci_bus)
	    if(pci->bus > 7)
	      return 1; /* done */
	  }
      }
    return 0;
  }

int pci_list(void)
  {
    pci_t pci;
    int err;
    unsigned long id;
    
    /* check for PCI BIOS */
    if (pci_detect())
      return 1;

    /* display numeric ID of all PCI devices detected */
    memset(&pci, 0, sizeof(pci));
    do
      {
	/* 00=PCI_VENDOR_ID */
	err = pci_read_config_dword(&pci, 0x00, &id);
	if (err)
	{
	  printf("Error 0x%02x reading PCI config\n",err);
	  return 1;
	}
	/* anything there? */
	if (id != 0xFFFFFFFF)
	  {
	    printf("bus %d, device %d, function %d: ID=%04x:%04x\n", pci.bus,pci.dev,pci.fn,id & 0xFFFF, id >> 16);
	  }
      } while(!pci_iterate(&pci));

    return 0;
  }


